package demoMod.gdxform.core;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
import com.badlogic.gdx.utils.Disposable;
import demoMod.gdxform.abstracts.Container;
import demoMod.gdxform.interfaces.Element;
import demoMod.gdxform.interfaces.ISubscriber;
import demoMod.gdxform.interfaces.Renderable;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class FormManager implements Renderable, Disposable {
    private final List<Container<? extends Element>> containerList;
    private final ActionManager actionManager = new ActionManager();
    private static FormManager inst;
    private final EventHooks eventHooks;
    private final FrameBuffer frameBuffer;
    private boolean paused = false;

    private FormManager() {
        containerList = new ArrayList<>();
        eventHooks = new EventHooks();
        frameBuffer = new FrameBuffer(Pixmap.Format.RGBA8888, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false);
        reassignEventHooks();
    }

    public static FormManager getInstance() {
        if (inst == null) {
            inst = new FormManager();
        }
        return inst;
    }

    public EventHooks getEventHooks() {
        return eventHooks;
    }

    public void reassignEventHooks() {
        Gdx.input.setInputProcessor(eventHooks);
    }

    public void update() {
        List<Container<? extends Element>> tmpList = new ArrayList<>(containerList);
        Collections.reverse(tmpList);
        for (Container<? extends Element> container : tmpList) {
            container.update();
        }
        actionManager.update();
        eventHooks.update();
    }

    @Override
    public void render(SpriteBatch sb) {
        for (Container<? extends Element> container :containerList) {
            container.render(sb);
        }
    }

    public FrameBuffer getFrameBuffer() {
        return frameBuffer;
    }

    public void addContainer(Container<? extends Element> container) {
        this.containerList.add(container);
        if (this.containerList.size() >= 2) {
            if (this.containerList.get(this.containerList.size() - 2).compareTo(container) > 0) {
                sortContainers();
            }
        }
        if (container.hasFocus()) eventHooks.setCurrentFocusedElement(container);
    }

    public void removeContainer(Container<? extends Element> container) {
        this.actionManager.addToSequenceBot(new AbstractAction.Builder().setActionBody(duration -> {
            containerList.remove(container);
            container.dispose();
            return true;
        }).build());
    }

    public void moveToTop(Container<? extends Element> container) {
        if (containerList.contains(container)) {
            this.actionManager.addToSequenceBot(new AbstractAction.Builder().setActionBody(duration -> {
                containerList.remove(container);
                if (!containerList.isEmpty()) {
                    int t = containerList.get(containerList.size() - 1).getPriority();
                    containerList.get(containerList.size() - 1).setPriority(container.getPriority());
                    container.setPriority(t);
                }
                containerList.add(container);
                return true;
            }).build());
        }
    }

    public List<Container<? extends Element>> getContainerList() {
        return new ArrayList<>(containerList);
    }

    public void sortContainers() {
        containerList.sort(Comparator.comparingInt(ISubscriber::getPriority));
    }

    public Container<? extends Element> getContainerById(String id) {
        for (Container<? extends Element> container : containerList) {
            if (container.getId().equals(id)) {
                return container;
            }
        }
        return null;
    }

    public void pause() {
        this.paused = true;
    }

    public void resume() {
        this.paused = false;
    }

    @Override
    public void dispose() {
        for (Container<? extends Element> container :containerList) {
            container.dispose();
        }
        frameBuffer.dispose();
    }
}
